home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir31
/
tnypl211.zip
/
MODPL16.ASM
< prev
next >
Wrap
Assembly Source File
|
1994-06-21
|
63KB
|
2,116 lines
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
; 16-bit Tiny MOD Player for Borland C++ 3.1 C compiler
; Version 2.11a June 15th, 1994
;
; Copyright 1993,94 Carlos Hasan
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
ideal
model large,c
p386
smart
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; EQUATES AND PUBLICS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
MAXVOICES = 8 ; number of voices
DMABUFLEN = 1024 ; DMA buffer length (multiple of 64)
VOLBUFLEN = 66*256 ; volume table length
MIXBUFLEN = DMABUFLEN+2048 ; mixing/boosting buffer length
TIMERRATE = 17000 ; timer interrupt rate in ticks
global MODPlayModule:proc
global MODStopModule:proc
global MODPlaySample:proc
global MODStopSample:proc
global MODSetPeriod:proc
global MODSetVolume:proc
global MODSetMusicVolume:proc
global MODSetSampleVolume:proc
global MODDetectCard:proc
global MODPoll:proc
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; STRUCTURES
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
struc module ; module structure
numtracks dw ? ; number of tracks
orderlen dw ? ; order length
orders db 128 dup (?) ; order list
patterns dd 128 dup (?) ; pattern addresses
sampptr dd 32 dup (?) ; sample start addresses
sampend dd 32 dup (?) ; sample end addresses
samploop dd 32 dup (?) ; sample loop point addresses
sampvolume db 32 dup (?) ; sample default volumes
ends module
struc sample ; sample structure
period dw ? ; default period
volume dw ? ; default volume
datalen dd ? ; sample data length
dataptr dd ? ; sample data address
ends sample
struc track ; track structure
note dw ? ; note index
period dw ? ; period value
inst db ? ; instrument
volume db ? ; volume
effect dw ? ; effect
destperiod dw ? ; toneporta wanted period
tonespeed db ? ; toneporta speed
vibparm db ? ; vibrato depth/rate
vibpos db ? ; vibrato wave position
tremparm db ? ; tremolo depth/rate
trempos db ? ; tremolo wave position
db ? ; alignment
arptable dw 3 dup (?) ; arpeggio periods
ends track
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DATA
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Module Player data
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
udataseg
moduleptr dd ? ; current module address
pattptr dd ? ; current playing pattern address
orderpos db ? ; order position
orderlen db ? ; order length
pattrow db ? ; pattern row
tempo db ? ; tempo
tempocount db ? ; tempo counter
bpm db ? ; beats per minute
musicvolume db ? ; music channels volume
samplevolume db ? ; sample channels volume
numtracks dw ? ; number of tracks
tracks track MAXVOICES dup (?)
pitchtable dd 3425 dup (?) ; period to pitch table
; Amiga period table
dataseg
periodtable dw 0
dw 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812
dw 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
dw 856,808,762,720,678,640,604,570,538,508,480,453
dw 428,404,381,360,339,320,302,285,269,254,240,226
dw 214,202,190,180,170,160,151,143,135,127,120,113
dw 107,101,95,90,85,80,75,71,67,63,60,56
dw 53,50,47,45,42,40,37,35,33,31,30,28
; Sinus wave table
sintable db 0,25,50,74,98,120,142,162,180,197,212,225
db 236,244,250,254,255,254,250,244,236,225
db 212,197,180,162,142,120,98,74,50,25
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster driver data
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
udataseg
; Voices programmable parameters
voicepos dd MAXVOICES dup (?)
voiceend dd MAXVOICES dup (?)
voiceloop dd MAXVOICES dup (?)
voicefrac dd MAXVOICES dup (?)
voicepitch dd MAXVOICES dup (?)
voicevolume dd MAXVOICES dup (?)
; Internal driver data
dataseg
mixbuffer dw ? ; mixing buffer address
boosttable dw ? ; boosting table address
voltable dw ? ; volume table address
numvoices dw ? ; number of active voices
mixfreq dw ? ; playback frequency
ioaddr dw ? ; card I/O port address
irqnum db ? ; card IRQ level
drqnum db ? ; card DMA channel
timerproc dw ? ; timer callback address
timeracc dw ? ; timer callback accumulator
timerspeed dw ? ; timer callback speed
bufsel dw ? ; DOS memory block selector
bufptr dw ? ; DMA buffer address
bufoff dw ? ; double buffer offset
oldirqoff dw ? ; old IRQ vector address
oldirqsel dw ?
oldtimeroff dw ? ; old timer IRQ0 vector address
oldtimersel dw ?
oldtimeracc dw ? ; old timer accumulator
manualmode db ? ; timer/manual polling mode
playing db 0 ; playing/stopped status
ufardata fardataseg
db DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15 dup (?)
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; CODE
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
codeseg
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Copyright Strings
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
db '16-bit Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan',0
db 'Compiled on: ',??date,' ',??time,0
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Module Player stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPlayModule - start playing a music module
; In:
; Song = module address
; Chans = number of channels
; Rate = playback rate
; Port = port address
; irq = irq number
; dma = dma channel
; mode = polling mode
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPlayModule Song:dword,Chans:byte,Rate:word,Port:word,IRQ:byte,DRQ:byte,Mode:byte
pushad
push es
; setup the music module address
les si,[Song]
mov [word low moduleptr],si
mov [word high moduleptr],es
; setup the sound card driver
mov ax,[Rate]
mov bl,[Chans]
mov dx,[Port]
mov cl,[IRQ]
mov ch,[DRQ]
mov bh,[Mode]
call mixinit
jc playmoduled0
; build the period to pitch table (16.16 fixed point values)
movzx ebx,ax
mov eax,8363*428
xor edx,edx
shld edx,eax,16
shl eax,16
div ebx
mov esi,eax
lea di,[pitchtable]
mov cx,3425
xor bx,bx
playmodulel0:
inc bx
xor edx,edx
mov eax,esi
div ebx
mov [di],eax
add di,4
loop playmodulel0
; setup global volumes for music and sample channels
mov [musicvolume],255
mov [samplevolume],255
; clear the module player track structures
push es
mov ax,ds
mov es,ax
cld
lea di,[tracks]
mov cx,MAXVOICES*(size track)
xor al,al
rep stosb
pop es
; check if there is a module to playback
xor ax,ax
mov [numtracks],ax
mov si,[word low moduleptr]
or si,[word high moduleptr]
test si,si
clc
je playmoduled0
; setup player interpreter variables
les si,[moduleptr]
mov ax,[es:si+module.orderlen]
mov [orderlen],al
mov ax,[es:si+module.numtracks]
mov [numtracks],ax
mov [tempo],6
mov [bpm],125
mov [orderpos],0
mov [tempocount],0
mov [pattrow],40h
; setup the player callback timer routine
lea dx,[pollmodule]
call mixsettimerproc
mov dl,[bpm]
call mixstarttimer
clc
playmoduled0:
pop es
popad
sbb ax,ax
ret
endp MODPlayModule
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODStopModule - shut down the music system
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODStopModule
pushad
call mixstoptimer ; stop the timer callback
call mixdone ; shutdown the SB stuff
popad
ret
endp MODStopModule
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPoll - polls the music system in manual mode
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPoll
pushad
cmp [manualmode],0 ; call the polling routine only
je modpolld0 ; if we are using the manual
cmp [playing],0 ; polling mode (and if the driver
je modpolld0 ; is active).
call mixpoll
modpolld0:
popad
ret
endp MODPoll
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPlaySample - play sample instrument
; In:
; voice = voice number
; sample = sample address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPlaySample Voice:word,SamplePtr:dword
pushad
push es
cli
; get the voice number and track address
movzx ebx,[Voice]
les di,[SamplePtr]
mov si,bx
imul si,size track
; set the voice pitch value
movzx eax,[es:di+sample.period]
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
; set the voice sample parameters
mov eax,[es:di+sample.dataptr]
mov [4*ebx+voicepos],eax
add eax,[es:di+sample.datalen]
mov [word low 4*ebx+voiceend],ax
mov [word low 4*ebx+voiceloop],ax
; set the voice and track volumes
mov ax,[es:di+sample.volume]
mov [si+tracks.volume],al
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
sti
pop es
popad
ret
endp MODPlaySample
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODStopSample - stop the playing sample
; In:
; voice = voice number
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODStopSample Voice:word
pushad
cli
; get the voice number
movzx ebx,[Voice]
xor eax,eax
; clear the voice sample parameters
mov [4*ebx+voicepos],eax
mov [word low 4*ebx+voiceend],ax
mov [word low 4*ebx+voiceloop],ax
sti
popad
ret
endp MODStopSample
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetPeriod - set the voice period value
; In:
; voice = voice number
; period = period value (113-856)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetPeriod Voice:word,Period:word
pushad
cli
; get the voice number and period value
movzx ebx,[Voice]
movzx eax,[Period]
; set the voice pitch value
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
sti
popad
ret
endp MODSetPeriod
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetVolume - set the voice volume level
; In:
; voice = voice number
; volume = volume level (0-64)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetVolume Voice:word,Volume:word
pushad
cli
; get the voice number and track address
movzx ebx,[Voice]
mov ax,[Volume]
mov si,bx
imul si,size track
; set the voice and track volume
mov [si+tracks.volume],al
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
sti
popad
ret
endp MODSetVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetMusicVolume - set the global music volume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetMusicVolume Volume:word
pushad
cli
; set new music volume
mov ax,[Volume]
mov [musicvolume],al
; update all the music voices
lea si,[tracks]
xor ebx,ebx
setmusicvolumel0:
mov al,[si+track.volume]
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
add si,size track
inc bx
cmp bx,[numtracks]
jb setmusicvolumel0
sti
popad
ret
endp MODSetMusicVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetSampleVolume - set the global sample volume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetSampleVolume Volume:word
pushad
cli
; set the sample volume
mov ax,[Volume]
mov [samplevolume],al
; update all the sample voices
lea si,[tracks]
xor ebx,ebx
setsamplevolumel0:
cmp bx,[numtracks]
jb setsamplevolumef0
mov al,[si+track.volume]
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
setsamplevolumef0:
add si,size track
inc bx
cmp bx,MAXVOICES
jb setsamplevolumel0
sti
popad
ret
endp MODSetSampleVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODDetectCard - detect the Sound Blaster configuration
; Out:
; Port = I/O Port
; IRQ = IRQ level
; DRQ = DMA channel
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODDetectCard Port:dword,IRQ:dword,DRQ:dword
pushad
push es
; call the lowlevel autodetection routine
call mixdetect
; set the parameters in the user variables
les di,[Port]
mov [es:di],dx
les di,[IRQ]
mov [es:di],cl
les di,[DRQ]
mov [es:di],ch
pop es
popad
sbb ax,ax
ret
endp MODDetectCard
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; pollmodule - polls the module player
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
pollmodule:
pushad
push es gs
dec [tempocount] ; decrease the tempo counter
jle pollmodulef0
lea si,[tracks] ; while in the same pattern row
xor ebx,ebx ; update the track effects.
pollmodulel0:
call updatechannel
add si,size track
inc bx
cmp bx,[numtracks]
jb pollmodulel0
pop gs es
popad
ret
pollmodulef0: ; advance to the next pattern row.
mov al,[tempo] ; update the tempo counter
mov [tempocount],al
xor edx,edx
lgs dx,[moduleptr] ; get module and pattern address
les di,[pattptr]
cmp [pattrow],40h ; need to advance to the next order?
jb pollmodulef2
xor eax,eax ; reset the pattern row
mov [pattrow],al
mov al,[orderpos] ; if we are at the end of the order
cmp al,[orderlen] ; list, loop to the beginning
jb pollmodulef1
xor al,al
mov [orderpos],al
pollmodulef1:
inc [orderpos] ; get the new pattern address
movzx eax,[gs:edx+eax+module.orders]
les di,[gs:edx+4*eax+module.patterns]
pollmodulef2:
inc [pattrow] ; increase pattern row number
lea si,[tracks]
xor ebx,ebx ; read and interpret the next
pollmodulel1: ; pattern row of events
call readchannel
add si,size track
add di,4
inc bx
cmp bx,[numtracks]
jb pollmodulel1
mov [word low pattptr],di ; save pattern row address
mov [word high pattptr],es
pop gs es
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; readchannel - read the next note event from the pattern sheet
; In:
; EBX = voice number
; DS:SI = track address
; ES:DI = pattern address
; GS:EDX = module address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
readchannel:
pushad
; check for new sample number . . .
mov al,[es:di+1]
test al,al
je readchannelf0
mov [si+track.inst],al
movzx eax,al
mov al,[gs:edx+eax+module.sampvolume]
mov [si+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
; check for new note pitch . . .
readchannelf0:
mov al,[es:di]
test al,al
je readchannelf1
movzx eax,al
mov [si+track.note],ax
cmp [byte es:di+3],03h
je readchannelf1
mov ax,[2*eax+periodtable]
mov [si+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
movzx eax,[si+track.inst]
mov ecx,[gs:edx+4*eax+module.sampptr]
mov [4*ebx+voicepos],ecx
mov ecx,[gs:edx+4*eax+module.sampend]
mov [word low 4*ebx+voiceend],cx
mov ecx,[gs:edx+4*eax+module.samploop]
mov [word low 4*ebx+voiceloop],cx
; check the new track effect . . .
readchannelf1:
mov dx,[es:di+2]
mov [si+track.effect],dx
movzx eax,dh
and al,0Fh
call [2*eax+efxtable]
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; updatechannel - update the track using the current effect
; In:
; EBX = voice number
; DS:SI = track address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
updatechannel:
pushad
mov dx,[si+track.effect]
movzx eax,dh
and al,0Fh
call [2*eax+efxtable2]
popad
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Protracker effects stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Effect jump tables
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
align 2
label efxtable word
dw efxarpeggio ; 0 - arpeggio
dw efxnull ; 1 - porta up
dw efxnull ; 2 - porta down
dw efxtoneporta ; 3 - tone porta
dw efxvibrato ; 4 - vibrato
dw efxnull ; 5 - tone+slide
dw efxnull ; 6 - vibrato+slide
dw efxtremolo ; 7 - tremolo
dw efxnull ; 8 - unused
dw efxsampoffset ; 9 - sample offset
dw efxnull ; A - volume slide
dw efxpattjump ; B - pattern jump
dw efxsetvolume ; C - set volume
dw efxbreak ; D - break pattern
dw efxnull ; E - extra effects
dw efxsetspeed ; F - set speed
label efxtable2 word
dw efxarpeggio2 ; 0 - arpeggio
dw efxportaup ; 1 - porta up
dw efxportadown ; 2 - porta down
dw efxtoneporta2 ; 3 - tone porta
dw efxvibrato2 ; 4 - vibrato
dw efxtoneslide ; 5 - tone+slide
dw efxvibslide ; 6 - vibrato+slide
dw efxtremolo2 ; 7 - tremolo
dw efxnull ; 8 - unused
dw efxnull ; 9 - sample offset
dw efxvolslide ; A - volume slide
dw efxnull ; B - pattern jump
dw efxnull ; C - set volume
dw efxnull ; D - break pattern
dw efxnull ; E - extra effects
dw efxnull ; F - set speed
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxnull - dummy effect
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxnull:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxarpeggio - arpeggio
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxarpeggio:
test dl,dl
je efxnull
mov dh,dl
and dl,0Fh
shr dh,4
movzx eax,[si+track.note]
mov cx,[2*eax+periodtable]
mov [si+track.arptable],cx
add al,dh
mov cx,[2*eax+periodtable]
mov [si+2+track.arptable],cx
sub al,dh
add al,dl
mov cx,[2*eax+periodtable]
mov [si+4+track.arptable],cx
ret
efxarpeggio2:
test dl,dl
je efxnull
movzx eax,[si+track.arptable]
xchg [si+4+track.arptable],ax
xchg [si+2+track.arptable],ax
mov [si+track.arptable],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxportaup - slides the pitch up
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxportaup:
xor dh,dh
movzx eax,[si+track.period]
sub ax,dx
cmp ax,28
jge efxportaupf0
mov ax,28
efxportaupf0:
mov [si+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxportadown - slides the pitch down
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxportadown:
xor dh,dh
movzx eax,[si+track.period]
add ax,dx
cmp ax,3424
jle efxportadownf0
mov ax,3424
efxportadownf0:
mov [si+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtoneporta - tone portamento
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtoneporta:
test dl,dl
jne efxtoneportaf0
mov dl,[si+track.tonespeed]
efxtoneportaf0:
mov [si+track.tonespeed],dl
mov [si+track.effect],dx
movzx eax,[si+track.note]
mov ax,[2*eax+periodtable]
mov [si+track.destperiod],ax
ret
efxtoneporta2:
xor dh,dh
movzx eax,[si+track.period]
mov cx,[si+track.destperiod]
cmp ax,cx
je efxnull
jg efxtoneportaf1
add ax,dx
cmp ax,cx
jle efxtoneportaf2
mov ax,cx
efxtoneportaf2:
mov [si+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
efxtoneportaf1:
sub ax,dx
cmp ax,cx
jge efxtoneportaf3
mov ax,cx
efxtoneportaf3:
mov [si+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvibrato - pitch vibrato
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvibrato:
mov al,[si+track.vibparm]
mov ah,al
and ax,0F00Fh
test dl,0Fh
jne efxvibratof0
or dl,al
efxvibratof0:
test dl,0F0h
jne efxvibratof1
or dl,ah
efxvibratof1:
mov [si+track.vibparm],dl
mov [si+track.effect],dx
ret
efxvibrato2:
mov dh,dl
and dx,0F00Fh
shr dh,2
mov al,[si+track.vibpos]
add [si+track.vibpos],dh
mov dh,al
shr al,2
and eax,1Fh
mov al,[eax+sintable]
mul dl
shr ax,7
test dh,dh
jge efxvibratof2
neg ax
efxvibratof2:
add ax,[si+track.period]
cmp ax,28
jge efxvibratof3
mov ax,28
efxvibratof3:
cmp ax,3424
jle efxvibratof4
mov ax,3424
efxvibratof4:
movzx eax,ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtoneslide - volume slide and continue last portamento
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtoneslide:
call efxvolslide
mov dl,[si+track.tonespeed]
jmp efxtoneporta
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvibslide - volume slide and continue last pitch vibrato
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvibslide:
call efxvolslide
mov dl,[si+track.vibparm]
jmp efxvibrato2
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtremolo - volume vibrato
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtremolo:
mov al,[si+track.tremparm]
mov ah,al
and ax,0F00Fh
test dl,0Fh
jne efxtremolof0
or dl,al
efxtremolof0:
test dl,0F0h
jne efxtremolof1
or dl,ah
efxtremolof1:
mov [si+track.tremparm],dl
mov [si+track.effect],dx
ret
efxtremolo2:
mov dh,dl
and dx,0F00Fh
shr dh,2
mov al,[si+track.trempos]
add [si+track.trempos],dh
mov dh,al
shr al,2
and eax,1Fh
mov al,[eax+sintable]
mul dl
shr ax,6
test dh,dh
jge efxtremolof2
neg ax
efxtremolof2:
add al,[si+track.volume]
jge efxtremolof3
xor al,al
efxtremolof3:
cmp al,40h
jle efxtremolof4
mov al,40h
efxtremolof4:
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsampoffset - set the sample offset
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsampoffset:
movzx eax,[si+track.inst]
xor esi,esi
lgs si,[moduleptr]
mov eax,[gs:esi+4*eax+module.sampptr]
mov dh,dl
xor dl,dl
add ax,dx
mov [word low 4*ebx+voicepos],ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvolslide - volume slide
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvolslide:
mov al,[si+track.volume]
mov dh,dl
shr dl,4
je efxvolslidef0
add al,dl
cmp al,40h
jle efxvolslidef1
mov al,40h
efxvolslidef1:
mov [si+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
efxvolslidef0:
sub al,dh
jge efxvolslidef2
xor al,al
efxvolslidef2:
mov [si+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxpattjump - jump to order pattern
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxpattjump:
mov [orderpos],dl
mov [pattrow],40h
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsetvolume - set volume
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsetvolume:
mov al,dl
mov [si+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxbreak - break pattern
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxbreak:
mov [pattrow],40h
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsetspeed - set the tempo or BPM speed
; In:
; EBX = voice number
; DS:SI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsetspeed:
test dl,dl
je efxnull
cmp dl,20h
jae efxsetbpm
mov [tempo],dl
mov [tempocount],dl
ret
efxsetbpm:
mov [bpm],dl
call mixstarttimer
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster Driver highlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixinit - initialize the sound driver
; In:
; AX = mixing speed in hertz
; BL = number of voices
; DX = I/O port address
; CL = IRQ level
; CH = DRQ channel
; BH = polling mode
; Out:
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixinit:
pushad
push es
cmp [playing],0
stc
jne mixinitd0
; setup sound card parameters
mov [manualmode],bh
xor bh,bh
mov [mixfreq],ax
mov [numvoices],bx
mov [ioaddr],dx
mov [irqnum],cl
mov [drqnum],ch
; check if the sound card is present
call sbreset
jc mixinitd0
; setup timer and double buffer variables
mov [timerproc],offset nulltimer
xor ax,ax
mov [bufoff],ax
mov [timeracc],ax
mov [timerspeed],256
; clear voice parameters
push es
mov ax,ds
mov es,ax
cld
lea di,[voicepos]
mov cx,6*MAXVOICES
xor eax,eax
rep stosd
pop es
; allocate conventional memory for the DMA buffer, the volume table,
; the mixing buffer and the boosting table.
; NOTE: there are problems allocating memory using INT 21h AH=48h under
; Borland C++ 3.1 so I am using an static data segment.
mov ax,fardataseg
mov [bufsel],ax
movzx eax,ax
shl eax,4
; set the address of the mixing buffer and boosting table
mov [mixbuffer],0
mov [boosttable],DMABUFLEN
add eax,DMABUFLEN+2048
; get the address of the DMA buffer and volume table
mov ecx,DMABUFLEN
lea edx,[eax+ecx]
; check for cross-pages in the DMA buffer and align the Volume table
mov esi,eax
add si,cx
jnc mixinitf0
mov edx,eax
add eax,VOLBUFLEN
mixinitf0:
movzx esi,[bufsel]
shl esi,4
sub eax,esi
sub edx,esi
add dx,255
xor dl,dl
mov [bufptr],ax
mov [voltable],dx
; clear DMA buffer with centered samples
push es
cld
mov es,[bufsel]
mov di,[bufptr]
mov cx,DMABUFLEN
mov al,80h
rep stosb
pop es
; build volume table and boosting table
push es
mov cl,6
mov es,[bufsel]
mov di,[voltable]
xor bx,bx
mixinitl0:
mov al,bl
imul bh
sar ax,cl
mov [es:di],al
inc di
inc bl
jne mixinitl0
inc bh
cmp bh,40h
jbe mixinitl0
pop es
push es
cld
mov es,[bufsel]
mov di,[boosttable]
mov cx,768
xor ax,ax
rep stosb
mov cx,512
mixinitl1:
mov [es:di],ah
add ax,80h
inc di
loop mixinitl1
mov cx,768
dec al
rep stosb
pop es
; initialize the sound card for output
call dmasetup
call irqsetup
call sbsetup
; dont use the timer interrupt for manual polling mode
cmp [manualmode],0
jne mixinitf1
; install timer interrupt to poll the driver
push es
mov ax,cs
mov es,ax
lea bx,[mixtimer]
mov cl,0
call irqsetvect
mov [oldtimeroff],bx
mov [oldtimersel],es
pop es
; set the timer frequency to 70 hertz
cli
mov al,36h
out 43h,al
mov ax,TIMERRATE
out 40h,al
mov al,ah
out 40h,al
sti
; set driver playing status
mixinitf1:
mov [playing],1
clc
mixinitd0:
pop es
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixdone - deinitialize the sound driver
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixdone:
pushad
push es
cmp [playing],1
jne mixdoned0
; the timer interrupt was modified if we are using the timer polling mode
cmp [manualmode],0
jne mixdonef0
; restore the timer frequency to 18.2 hertz
cli
mov al,36h
out 43h,al
xor al,al
out 40h,al
out 40h,al
sti
; deinstall timer interrupt used to poll the driver
push es
mov bx,[oldtimeroff]
mov es,[oldtimersel]
mov cl,0
call irqsetvect
pop es
; deinitialize the sound card output
mixdonef0:
call sbdone
call irqdone
call dmadone
; set driver stopped status
mov [playing],0
mixdoned0:
pop es
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixtimer - timer interrupt routine used to poll the sound driver
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixtimer:
push ax
push ds
mov ax,@data
mov ds,ax
call mixpoll ; poll the sound system
add [oldtimeracc],TIMERRATE
jnc mixtimerf0 ; time to call the old IRQ0 vector?
pushf ; yes, jump to the old IRQ0 service
call [dword oldtimeroff]
jmp mixtimerd0
mixtimerf0:
mov al,20h ; nope, send PIC acknowledge and exit
out 20h,al
mixtimerd0:
pop ds
pop ax
iret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixdetect - detect the sound card configuration
; Out:
; DX = I/O Port
; CL = IRQ level
; CH = DMA channel
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixdetect:
cmp [playing],1 ; do not try to autodetect
stc ; if we are already playing
je mixdetectd0
call sbdetect
mixdetectd0:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixsettimerproc - set the timer procedure
; In:
; CS:DX = timer routine address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixsettimerproc:
mov [timerproc],dx ; set the timer callback address
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixstarttimer - start the timer at the specified speed
; In:
; DL = timer speed in beats per minute (BPMs)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixstarttimer:
push ax
push bx
push dx
mov bh,dl ; set the timer callback speed
xor bl,bl ; to 24/60*BPM hertz
mov ax,[mixfreq]
mov dx,0280h
mul dx
div bx
mov [timerspeed],ax
pop dx
pop bx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixstoptimer - stop the timer routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixstoptimer:
mov [timerproc],offset nulltimer
nulltimer:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixvoice - mixes the voice samples
; In:
; EBX = voice number (*4)
; CX = number of samples
; ES:DI = buffer address
; Out:
; ES:DI = buffer end address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixvoice:
macro mixcode OPCODE
local mixcodel0,mixjmptable
push eax
push bx
push cx
push dx
push bp
push si
push ds
push es
push bx
mov ax,[voltable]
mov dl,[byte bx+3+voicefrac]
mov dh,[byte bx+1+voicepitch]
mov bp,[word bx+2+voicepitch]
add ah,[byte bx+voicevolume]
les si,[bx+voicepos]
mov ds,[bufsel]
mov bx,ax
movzx eax,cl
and al,31
shr cx,5
movzx edi,di
lea edi,[edi+2*eax-2*32]
jmp [2*eax+mixjmptable]
align 2
mixcodel0:
I=0
rept 32
CODESTART=$
mov bl,[es:si]
add dl,dh
movsx ax,[byte ds:bx]
adc si,bp
OPCODE [ds:di+I],ax
CODELEN=$-CODESTART
I=I+2
endm
add di,2*32
dec cx
jge mixcodel0
pop bx
pop es
pop ds
mov [word low bx+voicepos],si
mov [byte bx+3+voicefrac],dl
pop si
pop bp
pop dx
pop cx
pop bx
pop eax
ret
align 2
label mixjmptable word
I=CODESTART+CODELEN
rept 32
dw I
I=I-CODELEN
endm
endm
test bx,bx
je mixvoicef0
mixcode add
mixvoicef0:
mixcode mov
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixvoices - mixes all the voices
; In:
; ES:DI = buffer address
; CX = number of samples
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixvoices:
pushad
xor ebx,ebx
mixvoicesl0:
push bx
push cx
push di
shl bx,2
mov bp,cx
mixvoicesl1:
mov cx,bp
mov dx,[word low bx+voicepos]
mov ax,[word low bx+voiceend]
cmp dx,ax
jb mixvoicesf0
sub dx,ax
add dx,[word low bx+voiceloop]
cmp dx,ax
jae mixvoicesc0
mov [word low bx+voicepos],dx
mixvoicesf0:
sub ax,dx
shl eax,16
mov edx,[bx+voicefrac]
shr edx,16
sub eax,edx
movzx edx,cx
mov esi,[bx+voicepitch]
imul edx,esi
cmp edx,eax
jbe mixvoicesf1
dec eax
xor edx,edx
add eax,esi
adc edx,edx
div esi
mov cx,ax
mixvoicesf1:
call mixvoice
sub bp,cx
jg mixvoicesl1
pop di
pop cx
pop bx
inc bx
cmp bx,[numvoices]
jb mixvoicesl0
popad
ret
mixvoicesc0:
test bx,bx
jne mixvoicesc1
cld
xor ax,ax
rep stosw
mixvoicesc1:
pop di
pop cx
pop bx
inc bx
cmp bx,[numvoices]
jb mixvoicesl0
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixpoll - updates the output buffer
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixpoll:
pushad
push es
; get the 16 bit mixing buffer address
mov cx,DMABUFLEN/2
mov es,[bufsel]
mov di,[mixbuffer]
; check if we can fill the current half buffer with samples
call dmagetpos
cmp ax,cx
jae mixpollf3
cmp [bufoff],cx
je mixpollf4
jmp mixpolld0
mixpollf3:
cmp [bufoff],cx
je mixpolld0
; fill the mixing buffer and polls the timer callback routine
mixpollf4:
mov ax,[timeracc]
mov bp,cx
mixpolll0:
test ax,ax
jg mixpollf0
call [timerproc]
add ax,[timerspeed]
mixpollf0:
mov cx,ax
add cx,63
and cl,not 63
cmp cx,bp
jle mixpollf1
mov cx,bp
mixpollf1:
call mixvoices
add di,cx
add di,cx
sub ax,cx
sub bp,cx
jg mixpolll0
mov [timeracc],ax
; translate 16-bit signed samples to 8-bit unsigned samples
push ds
mov cx,DMABUFLEN/2
mov si,[mixbuffer]
movzx eax,[bufptr]
add ax,[bufoff]
xor [bufoff],cx
mov di,[boosttable]
add di,1024
shr cx,4
mov ds,[bufsel]
mixpolll2:
I=0
rept 4
mov bx,[si+8*I+4]
mov dl,[di+bx]
mov bx,[si+8*I+6]
mov dh,[di+bx]
shl edx,16
mov bx,[si+8*I]
mov dl,[di+bx]
mov bx,[si+8*I+2]
mov dh,[di+bx]
mov [eax+4*I],edx
I=I+1
endm
add si,2*16
add ax,16
dec cx
jg mixpolll2
pop ds
mixpolld0:
pop es
popad
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster DSP lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbwrite - send a command/data byte to the DSP chip
; In:
; AL = command/data byte
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbwrite:
push ax
push cx
push dx
mov dx,[ioaddr]
add dx,0Ch
mov ah,al
xor cx,cx ; wait until the write buffer
sbwritel0: ; status port (2XCh) bit 7 is clear
in al,dx
and al,80h
loopnz sbwritel0
mov al,ah ; write value in the write
out dx,al ; data port (2XCh)
pop dx
pop cx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbreset - reset the Sound Blaster DSP chip
; Out:
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbreset:
push ax
push bx
push cx
push dx
mov bx,64 ; try to reset upto 64 times
sbresetl1:
mov dx,[ioaddr]
add dx,06h
mov al,1 ; write 1 to the reset port (2X6h)
out dx,al
xor ah,ah ; wait at least 3 microseconds
sbresetl2:
in al,dx
dec ah
jne sbresetl2
xor al,al ; write 0 to the reset port (2X6h)
out dx,al
add dx,08h
mov cx,0400h ; wait until the data available
sbresetl0: ; status port (2XEh) bit 7 is set
in al,dx
and al,80h
loopz sbresetl0
sub dx,04h ; read the read data port (2XAh)
in al,dx
cmp al,0AAh
clc
je sbresetd0 ; check the ready byte value
dec bx
jne sbresetl1
stc
sbresetd0:
pop dx
pop cx
pop bx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbsetup - start the DMA output
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbsetup:
push ax
push dx
mov al,0D1h ; turn on the speaker
call sbwrite
mov al,40h ; set the playback rate
call sbwrite
mov ax,1000
mul ax
div [mixfreq]
neg ax
call sbwrite
mov al,14h ; start the lowspeed 8 bit DMA
call sbwrite ; mode transfer to the DAC
mov al,0FFh
call sbwrite
call sbwrite
pop dx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbdone - shut down the DMA output
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbdone:
push ax
call sbreset ; reset the DSP chip
mov al,0D3h
call sbwrite ; turn off the speaker
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbdetect - Detect the Sound Blaster I/O Port, IRQ level and DMA channel
; Out:
; DX = I/O port address
; CL = IRQ level
; CH = DMA channel
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbdetect:
mov dx,210h ; scan the ports 210h..260h
sbdetectl0:
mov [ioaddr],dx ; check if there is a SB card
call sbreset ; trying to reset the DSP chip
jnc sbdetectf0
add dx,10h
cmp dx,260h
jbe sbdetectl0
xor dx,dx
xor cx,cx
stc
ret
sbdetectf0:
push ax
push bx
push cx
push es
irp I,<2,3,5,7,10> ; install IRQ traps
push cs
pop es
lea bx,[irqtest&I]
mov cx,I
call irqsetvect
call irqsetmask
push bx
push es
endm
mov [irqnum],0
mov al,0F2h ; ask to the DSP to raise a IRQ
call sbwrite
xor cx,cx ; wait until some IRQ occurs
sbdetectl1:
cmp [irqnum],0
loope sbdetectl1
irp I,<10,7,5,3,2> ; deinstall IRQ traps
pop es
pop bx
mov cx,0100h+I
call irqsetmask
call irqsetvect
endm
pop es
pop cx
pop bx
pop ax
mov dx,[ioaddr] ; return the SB parameters
mov cl,[irqnum]
xor ch,ch
sub ch,cl
mov ch,1
cmc
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster DMA lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmasetup - setup the DMA buffer parameters
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmasetup:
push eax
push bx
push cx
push dx
mov bl,[drqnum]
mov al,bl
or al,04h ; reset the DMA channel
out 0Ah,al
out 0Ch,al ; clear the flip flop
mov al,bl
or al,58h ; set the autoinit mode
out 0Bh,al
movzx dx,bl
add dx,dx
push edx ; set the buffer address
movzx eax,[bufsel]
movzx edx,[bufptr]
shl eax,4
add eax,edx
pop edx
out dx,al
mov al,ah
out dx,al
inc dx
mov ax,DMABUFLEN ; set the buffer length
dec ax
out dx,al
mov al,ah
out dx,al
mov edx,82818387h ; set the buffer page
mov cl,bl
shl cl,3
shr edx,cl
xor dh,dh
shr eax,16
out dx,al
mov al,bl ; unlock the DMA channel
out 0Ah,al
pop dx
pop cx
pop bx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmadone - shut down the DMA controller
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmadone:
push ax
mov al,[drqnum] ; reset the DMA channel
or al,04h
out 0Ah,al
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmagetpos - return the DMA buffer relative position
; Out:
; AX = buffer relative position
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmagetpos:
push cx
push dx
out 0Ch,al ; clear the flip flop
mov dl,[drqnum]
xor dh,dh
add dl,dl
inc dl
in al,dx ; read the DMA counter
mov ah,al
in al,dx
xchg al,ah
dmagetposl0:
mov cx,ax ; read again the DMA counter
in al,dx
mov ah,al
in al,dx
xchg al,ah
sub cx,ax
cmp cx,+16 ; both values are near?
jg dmagetposl0 ; nope, try again
cmp cx,-16
jl dmagetposl0
neg ax ; get the position relative
add ax,DMABUFLEN ; to the start of the buffer
pop dx
pop cx
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster IRQ lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetvect - set the IRQ handler routine
; In:
; ES:BX = IRQ handler routine address
; CL = IRQ level
; Out:
; ES:BX = previous IRQ handler address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetvect:
push ax
push cx
push dx
; get the PIC interrupt master and slave base address
mov dx,0870h
; get the IDT interrupt slot number for the IRQ number
mov al,cl
cmp al,08h
jb irqsetvectf0
mov dh,dl
sub al,08h
irqsetvectf0:
add al,dh
; saves and change the IRQ handler routine
push ds
push es
push bx
mov ah,35h
int 21h
pop dx
pop ds
push es
push bx
mov ah,25h
int 21h
pop bx
pop es
pop ds
pop dx
pop cx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetmask - enable or disable the IRQ in the interrupt mask registers
; In:
; CL = IRQ level
; CH = enable (=0) or disable (=1)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetmask:
push ax
push dx
cli
in al,0A1h ; enable or disable the specified
mov ah,al ; IRQ using the PIC interrupt
in al,21h ; mask registers
mov dx,1
shl dx,cl
not dx
and ax,dx
mov dl,ch
shl dx,cl
or ax,dx
out 21h,al
mov al,ah
out 0A1h,al
sti
pop dx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetup - install the IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetup:
push ax
push bx
push cx
; set the IRQ handler routine and saves the previous vector
push es
mov ax,cs
mov es,ax
lea bx,[irqhandler]
mov cl,[irqnum]
call irqsetvect
mov [oldirqoff],bx
mov [oldirqsel],es
pop es
; enable the IRQ signals in the PIC interrupt mask
mov cl,[irqnum]
mov ch,0
call irqsetmask
pop cx
pop bx
pop ax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqdone - restores the old IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqdone:
push bx
push cx
; disable IRQ signals in the PIC interrupt mask register
mov cl,[irqnum]
mov ch,1
call irqsetmask
; restore the old IRQ handler routine
push es
mov bx,[oldirqoff]
mov es,[oldirqsel]
mov cl,[irqnum]
call irqsetvect
pop es
pop cx
pop bx
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqhandler - hardware IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqhandler:
push ax
push dx
push ds
mov ax,@data
mov ds,ax
; send acknowledge to the PIC controller
mov al,20h
cmp [irqnum],08h
jb irqhandlerf0
out 0A0h,al
irqhandlerf0:
out 20h,al
; send acknowledge to the DSP chip reading the 8 bit ack port (2XEh)
mov dx,[ioaddr]
add dx,0Eh
in al,dx
; restart the 8 bit DMA mode playback transfer
mov al,14h
call sbwrite
mov al,0FFh
call sbwrite
call sbwrite
pop ds
pop dx
pop ax
iret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqtest - testing hardware IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqtest:
push dx ; common IRQ test handler code
push ds ; used for autodetection
mov dx,@data
mov ds,dx
mov [irqnum],al ; save the IRQ level number
mov dx,[ioaddr] ; send acknowledge signal to the
add dx,0Eh ; DSP reading the ack port (2XEh)
in al,dx
mov al,20h ; send acknowledge to the PIC
cmp [irqnum],08h ; controllers
jb irqtestf0
out 0A0h,al
irqtestf0:
out 20h,al
pop ds
pop dx
pop ax
iret
irp I,<2,3,5,7,10> ; IRQ test handlers for each
irqtest&I: ; possible IRQ levels
push ax
mov ax,I
jmp irqtest
endm
end